package com.uyayo.oms.config;

import com.github.pagehelper.util.StringUtil;
import com.uyayo.oms.shiro.ManagerAuthenticationFilter;
import com.uyayo.oms.shiro.ManagerLoginFilter;
import com.uyayo.oms.shiro.MyShiroRealm;
import com.uyayo.system.authority.entity.SysResource;
import com.uyayo.system.authority.model.SysResourceCondition;
import com.uyayo.system.authority.service.SysResourceService;
import com.uyayo.system.config.entity.SysPermissionInit;
import com.uyayo.system.config.service.SysPermissionInitService;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by hfj
 * on 2017/4/23.
 */
@Configuration
public class ShiroConfig {
    @Autowired(required = false)
    private SysPermissionInitService sysPermissionInitService;

    @Autowired(required = false)
    private SysResourceService sysResourceService;

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.timeout}")
    private int timeout;

    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意：单独一个ShiroFilterFactoryBean配置是或报错的，因为在
     * 初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager
     *
     Filter Chain定义说明
     1、一个URL可以配置多个Filter，使用逗号分隔
     2、当设置多个过滤器时，全部验证通过，才视为通过
     3、部分过滤器可指定参数，如perms，roles
     *
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/oms/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/oms/index.html");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        //增加自定义过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("managerLogin", new ManagerLoginFilter());
        filters.put("managerAuthz", new ManagerAuthenticationFilter());
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl("/oms/login");
        filters.put("logout", logoutFilter);
        shiroFilterFactoryBean.setFilters(filters);

        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();

        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
       // filterChainDefinitionMap.put("/logout", "logout");
        //<!-- 过滤链定义，从上向下顺序执行，一般将 /**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        //自定义加载权限资源关系



//        filterChainDefinitionMap.put("/system/**", "perms[system:authority:resource:**]");
        //filterChainDefinitionMap.put("/oms/ucenter/permission/index", "perms[oms:ucenter:permission:query]");

        SysResourceCondition sysResourceCondition = new SysResourceCondition();
        sysResourceCondition.setStatus(1);
        List<SysResource> sysResources = sysResourceService.selectSysResource(sysResourceCondition);
        for(SysResource sysResource:sysResources) {
            if (StringUtil.isNotEmpty(sysResource.getUrl())) {
                String permission = "perms[" + sysResource.getPermission()+ "]";
                filterChainDefinitionMap.put(sysResource.getUrl(), permission);
            }
        }


        SysPermissionInit sysPermissionInit = new SysPermissionInit();
        sysPermissionInit.setStatus(1);
        List<SysPermissionInit> sysPermissionInits =  sysPermissionInitService.queryAll(sysPermissionInit);
        for(SysPermissionInit permissionInit:sysPermissionInits) {
            if (StringUtil.isNotEmpty(permissionInit.getUrl())) {
                filterChainDefinitionMap.put(permissionInit.getUrl(), permissionInit.getPermissionInit());
            }
        }

        //filterChainDefinitionMap.put("/**/index", "anon");
        //filterChainDefinitionMap.put("/**/login", "anon");
        //filterChainDefinitionMap.put("/**/logout", "logout");
        //filterChainDefinitionMap.put("/system/authority/resource/getMainMenu", "anon,managerLogin");
        //filterChainDefinitionMap.put("/system/**", "perms[system:**]");
        //filterChainDefinitionMap.put("/oms/**", "managerLogin");
        //filterChainDefinitionMap.put("/captcha/**", "anon");
        //filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        //设置realm.
        securityManager.setRealm(myShiroRealm());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    @Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCachingEnabled(true);
        myShiroRealm.setAuthenticationCachingEnabled(true);
        myShiroRealm.setAuthenticationCacheName("authenticationCache");
        myShiroRealm.setAuthorizationCachingEnabled(true);
        myShiroRealm.setAuthorizationCacheName("authorizationCache");
        //myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    /**
     * 凭证匹配器
     * （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     *  所以我们需要修改下doGetAuthenticationInfo中的代码;
     * ）
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次数，比如散列两次，相当于 md5(md5(""));

        return hashedCredentialsMatcher;
    }


    /**
     *  开启shiro aop注解支持.
     *  使用代理方式;所以需要开启代码支持;
     * @param securityManager
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host+":"+port);
        //redisManager.setPort(port);
        //redisManager.setExpire(1800);// 配置缓存过期时间
        redisManager.setTimeout(timeout);
        // redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());

        return redisCacheManager;
    }


    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /**
     * shiro session的管理
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

}
